﻿using System;
using System.Collections.Specialized;
using PostSharp.Aspects;
using PostSharp.Aspects.Advices;
using PostSharp.Extensibility;

namespace BusinessFramework.AOP
{
    [Serializable]
    [MulticastAttributeUsage(MulticastTargets.Class, Inheritance = MulticastInheritance.Strict, PersistMetaData=true)]
    public sealed class SetParentAttribute : InstanceLevelAspect
    {
        [OnLocationSetValueAdvice,
         MulticastPointcut(Targets = MulticastTargets.Property, 
             Attributes = MulticastAttributes.Instance | MulticastAttributes.NonAbstract)]
        public void OnPropertySet(LocationInterceptionArgs args)
        {
            var currentValue = args.GetCurrentValue();
            var value = args.Value;

            if (!args.Location.PropertyInfo.IsDefined(typeof(IgnoreTraversalAttribute), true))
            {
                if (!object.ReferenceEquals(value, currentValue))
                {
                    if (currentValue is IEntityBase)
                        ((IEntityBase) currentValue).Extensions.Parent = null;

                    if (value is IEntityBase && args.Instance is IEntityBase)
                        ((IEntityBase) value).Extensions.Parent = ((IEntityBase) args.Instance).Extensions;
                }
            }{
            if (value is INotifyCollectionChanged)
                {
                    if (currentValue is INotifyCollectionChanged)
                        ((INotifyCollectionChanged) currentValue).CollectionChanged -= OnCollectionChanged;

                    ((INotifyCollectionChanged) value).CollectionChanged += OnCollectionChanged;
                }
            }

            args.ProceedSetValue();
        }

        void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            var ex = ((IEntityBase) Instance).Extensions;

            if (e.Action == NotifyCollectionChangedAction.Add)
            {
                ((IEntityBase) e.NewItems[0]).Extensions.Parent = ex;
            }
            else if (e.Action == NotifyCollectionChangedAction.Remove)
            {
                ((IEntityBase)e.OldItems[0]).Extensions.Parent = null;
            }
            else if (e.Action == NotifyCollectionChangedAction.Replace)
            {
                ((IEntityBase)e.OldItems[0]).Extensions.Parent = null;
                ((IEntityBase)e.NewItems[0]).Extensions.Parent = ex;
            }
#if NET
            else if (e.Action == NotifyCollectionChangedAction.Move)
                ;
#endif
            else
            {
                throw new NotImplementedException();
            }
        }
    }
}